{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "slideshow": {
     "slide_type": "notes"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = 'all'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "<div class=\"jumbotron\">\n",
    "    <p class=\"display-1 h1\">多输入多输出通道</p>\n",
    "    <hr class=\"my-4\">\n",
    "    <p>主讲：李岩</p>\n",
    "    <p>管理学院</p>\n",
    "    <p>liyan@cumtb.edu.cn</p>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 多个输入通道"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 彩色图像可能有RGB三个通道，转换为灰度会丢失信息\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](channel0.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 当添加通道时，输入和隐藏的表示都变成了三维张量\n",
    "    - 例如，每个RGB输入图像具有$3\\times h\\times w$的形状\n",
    "- 将这个大小为$3$的轴称为**通道**（channel）维度"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "\\begin{problem}\\label{prob:channelsKernel}\n",
    "当存在多个输入通道时，如何计算卷积？\n",
    "\\end{problem}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 当输入包含多个通道时，需要构造一个与输入数据具有**相同输入通道数**的卷积核，以便与输入数据进行互相关运算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 假设输入的通道数为$c_i$，那么卷积核的输入通道数也需要为$c_i$\n",
    "- 如果卷积核的窗口形状是$k_h\\times k_w$。当$c_i>1$时，卷积核的每个输入通道将包含形状为$k_h\\times k_w$的张量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 这些张量$c_i$连结在一起可以得到形状为$c_i\\times k_h\\times k_w$的卷积核"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 由于输入和卷积核都有$c_i$个通道，可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算，再对**通道求和**（将$c_i$个结果相加）得到二维张量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "\\begin{example}\\label{example:twoChannels}\n",
    "一个具有两个输入通道的二维互相关运算的示例\n",
    "\\end{example}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "![](channel1.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "用公式表达\n",
    "- 输入$\\mathbf{X}$： $c_i × n_h \\times n_w$\n",
    "- 核$\\mathbf{W}$：$c_i × k_h × k_w$，偏移这里没有写，是个长度为$c_i$的一维向量。\n",
    "- 输出$\\mathbf{Y}$：$m_h × m_w$，**无论输入通道有多少，都是单输出通道**。\n",
    "\n",
    "$$Y = \\sum^{c_i}_{i=0} X_{i,:,:}★ W_{i,:,:}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "# 用代码实现多输入通道\n",
    "\n",
    "def corr2d_multi_in(X, K):\n",
    "    # 先遍历“X”和“K”的第0个维度（通道维度），再把它们加在一起\n",
    "    return sum(corr2d(x, k) for x, k in zip(X, K))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "def corr2d(X, K):\n",
    "    \"\"\"计算二维互相关运算\n",
    "        X是输入张量\n",
    "        K是卷积核张量\n",
    "    \"\"\"\n",
    "    h, w = K.shape # 核的形状\n",
    "    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) # 输出的形状\n",
    "    for i in range(Y.shape[0]):\n",
    "        for j in range(Y.shape[1]):\n",
    "            Y[i, j] = (X[i:i + h, j:j + w] * K).sum() # 卷积\n",
    "    return Y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "- 构造与上图中的值相对应的输入张量`X`和核张量`K`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">输入张量X <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">tensor</span><span style=\"font-weight: bold\">([[[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span>.<span style=\"font-weight: bold\">]</span>,\n",
       "         <span style=\"font-weight: bold\">[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">3</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">4</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">5</span>.<span style=\"font-weight: bold\">]</span>,\n",
       "         <span style=\"font-weight: bold\">[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">6</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">7</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">8</span>.<span style=\"font-weight: bold\">]]</span>,\n",
       "\n",
       "        <span style=\"font-weight: bold\">[[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">3</span>.<span style=\"font-weight: bold\">]</span>,\n",
       "         <span style=\"font-weight: bold\">[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">4</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">5</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">6</span>.<span style=\"font-weight: bold\">]</span>,\n",
       "         <span style=\"font-weight: bold\">[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">7</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">8</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">9</span>.<span style=\"font-weight: bold\">]]])</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "输入张量X \u001b[1;35mtensor\u001b[0m\u001b[1m(\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1;36m0\u001b[0m., \u001b[1;36m1\u001b[0m., \u001b[1;36m2\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m3\u001b[0m., \u001b[1;36m4\u001b[0m., \u001b[1;36m5\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m6\u001b[0m., \u001b[1;36m7\u001b[0m., \u001b[1;36m8\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\n",
       "        \u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1;36m1\u001b[0m., \u001b[1;36m2\u001b[0m., \u001b[1;36m3\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m4\u001b[0m., \u001b[1;36m5\u001b[0m., \u001b[1;36m6\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m7\u001b[0m., \u001b[1;36m8\u001b[0m., \u001b[1;36m9\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">核K <span style=\"color: #800080; text-decoration-color: #800080; font-weight: bold\">tensor</span><span style=\"font-weight: bold\">([[[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">0</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>.<span style=\"font-weight: bold\">]</span>,\n",
       "         <span style=\"font-weight: bold\">[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">3</span>.<span style=\"font-weight: bold\">]]</span>,\n",
       "\n",
       "        <span style=\"font-weight: bold\">[[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">1</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">2</span>.<span style=\"font-weight: bold\">]</span>,\n",
       "         <span style=\"font-weight: bold\">[</span><span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">3</span>., <span style=\"color: #008080; text-decoration-color: #008080; font-weight: bold\">4</span>.<span style=\"font-weight: bold\">]]])</span>\n",
       "</pre>\n"
      ],
      "text/plain": [
       "核K \u001b[1;35mtensor\u001b[0m\u001b[1m(\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1;36m0\u001b[0m., \u001b[1;36m1\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m2\u001b[0m., \u001b[1;36m3\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\n",
       "        \u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1;36m1\u001b[0m., \u001b[1;36m2\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m3\u001b[0m., \u001b[1;36m4\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m)\u001b[0m\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],\n",
    "               [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])\n",
    "K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])\n",
    "\n",
    "print(f'输入张量X {X}')\n",
    "\n",
    "print(f'核K {K}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"></pre>\n"
      ],
      "text/plain": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\n",
       "\u001b[1;35mtensor\u001b[0m\u001b[1m(\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m \u001b[1;36m56\u001b[0m.,  \u001b[1;36m72\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "        \u001b[1m[\u001b[0m\u001b[1;36m104\u001b[0m., \u001b[1;36m120\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m)\u001b[0m"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "corr2d_multi_in(X, K)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## 多个输出通道"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 可以有多个三维卷积核，每个核生成一个输出通道"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 输入$\\mathbf{X}$：$c_i × n_h \\times n_w$，与之前写法相同\n",
    "- 核$\\mathbf{W}$：$c_o \\times c_i × k_h × k_w$，注意这里多了$c_o$\n",
    "- 输出$\\mathbf{Y}$：$c_o \\times m_h × m_w$\n",
    " \n",
    "$$Y_{i,:,:} = X ★ W_{i,:,:,:} \\ \\ for \\ i = 1,... c_o$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 每个输出通道可以识别特定模式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "![](channel2.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "\\begin{example}\\label{example:multioutput}\n",
    "实现一个计算多个通道的输出的互相关函数\n",
    "\\end{example}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "def corr2d_multi_in_out(X, K):\n",
    "    # 迭代“K”的第0个维度，每次都对输入“X”执行互相关运算。\n",
    "    # 最后将所有结果都叠加在一起\n",
    "    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "- ```python\n",
    "    torch.stack(tensors, dim=0, *, out=None) → Tensor\n",
    "```\n",
    "\n",
    "    - 沿着一个新维度连接一系列张量\n",
    "    - 所有张量必须有相同的形状"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"></pre>\n"
      ],
      "text/plain": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\u001b[1;35mtorch.Size\u001b[0m\u001b[1m(\u001b[0m\u001b[1m[\u001b[0m\u001b[1;36m3\u001b[0m, \u001b[1;36m2\u001b[0m, \u001b[1;36m2\u001b[0m, \u001b[1;36m2\u001b[0m\u001b[1m]\u001b[0m\u001b[1m)\u001b[0m"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 通过将核张量K与K+1（K中每个元素加1）和K+2连接起来，构造了一个具有3个输出通道的卷积核\n",
    "\n",
    "K = torch.stack((K, K + 1, K + 2), 0)\n",
    "K.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"></pre>\n"
      ],
      "text/plain": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "\n",
       "\u001b[1;35mtensor\u001b[0m\u001b[1m(\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m\u001b[1m[\u001b[0m \u001b[1;36m56\u001b[0m.,  \u001b[1;36m72\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m104\u001b[0m., \u001b[1;36m120\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\n",
       "        \u001b[1m[\u001b[0m\u001b[1m[\u001b[0m \u001b[1;36m76\u001b[0m., \u001b[1;36m100\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m148\u001b[0m., \u001b[1;36m172\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m,\n",
       "\n",
       "        \u001b[1m[\u001b[0m\u001b[1m[\u001b[0m \u001b[1;36m96\u001b[0m., \u001b[1;36m128\u001b[0m.\u001b[1m]\u001b[0m,\n",
       "         \u001b[1m[\u001b[0m\u001b[1;36m192\u001b[0m., \u001b[1;36m224\u001b[0m.\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m]\u001b[0m\u001b[1m)\u001b[0m"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 对输入张量X与卷积核张量K执行互相关运算。现在的输出包含3个通道\n",
    "\n",
    "corr2d_multi_in_out(X, K)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## $1 \\times 1$卷积层"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- $k_h=k_w =1$是一个受欢迎的选择。它不识别空间模式，只是融合通道。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- $1\\times 1$卷积失去了卷积层的特有能力——在高度和宽度维度上，识别相邻元素间相互作用的能力"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "\\begin{example}\\label{example:1x1kernel}\n",
    "下图为例：输入3个神经元，输出2个神经元\n",
    "\\end{example}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "![](11kernel.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "输入和输出具有相同的高度和宽度，输出中的每个元素都是从输入图像中同一位置的元素的线性组合"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 相当于输入形状为$c_i\\times n_hn_w$，权重为$c_o×c_i$的全连接层"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "# 使用全连接层实现1x1卷积。注意，需要对输入和输出的数据形状进行调整\n",
    "\n",
    "def corr2d_multi_in_out_1x1(X, K):\n",
    "    c_i, h, w = X.shape\n",
    "    c_o = K.shape[0]\n",
    "    X = X.reshape((c_i, h * w))\n",
    "    K = K.reshape((c_o, c_i))\n",
    "    # 全连接层中的矩阵乘法\n",
    "    Y = torch.matmul(K, X)\n",
    "    return Y.reshape((c_o, h, w))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "当执行$1\\times 1$卷积运算时，上述函数相当于先前实现的互相关函数`corr2d_multi_in_out`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "输入张量X tensor([[[ 0.2704,  0.2940,  0.1996],\n",
      "         [ 0.6914,  0.4298,  0.2265],\n",
      "         [ 0.4663,  0.6717, -0.0618]],\n",
      "\n",
      "        [[-0.0922, -1.1194,  0.3954],\n",
      "         [ 0.3259, -0.6016,  0.5163],\n",
      "         [ 1.8032,  0.1330,  0.6792]],\n",
      "\n",
      "        [[-0.5218,  0.7794,  0.1832],\n",
      "         [ 2.4810,  0.8834, -1.2039],\n",
      "         [ 0.3733, -3.3564,  0.7075]]])\n",
      "卷积核 tensor([[[[-0.7737]],\n",
      "\n",
      "         [[ 0.9011]],\n",
      "\n",
      "         [[-0.2354]]],\n",
      "\n",
      "\n",
      "        [[[-1.0845]],\n",
      "\n",
      "         [[ 0.6264]],\n",
      "\n",
      "         [[-0.1117]]]])\n"
     ]
    }
   ],
   "source": [
    "# 用数据检验1x1卷积运算与互相关函数的一致\n",
    "\n",
    "X = torch.normal(0, 1, (3, 3, 3))\n",
    "K = torch.normal(0, 1, (2, 3, 1, 1))\n",
    "print(f'输入张量X {X}\\n卷积核 {K}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Y1 tensor([[[-0.1695, -1.4197,  0.1588],\n",
      "         [-0.8253, -1.0826,  0.5734],\n",
      "         [ 1.1762,  0.3903,  0.4933]],\n",
      "\n",
      "        [[-0.2927, -1.1071,  0.0107],\n",
      "         [-0.8229, -0.9416,  0.2123],\n",
      "         [ 0.5821, -0.2701,  0.4134]]])\n",
      "Y2 tensor([[[-0.1695, -1.4197,  0.1588],\n",
      "         [-0.8253, -1.0826,  0.5734],\n",
      "         [ 1.1762,  0.3903,  0.4933]],\n",
      "\n",
      "        [[-0.2927, -1.1071,  0.0107],\n",
      "         [-0.8229, -0.9416,  0.2123],\n",
      "         [ 0.5821, -0.2701,  0.4134]]])\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "2.868473529815674e-07"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y1 = corr2d_multi_in_out_1x1(X, K)\n",
    "Y2 = corr2d_multi_in_out(X, K)\n",
    "print(f'Y1 {Y1}\\nY2 {Y2}')\n",
    "float(torch.abs(Y1 - Y2).sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# 二维卷积层"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "- 输入**X**: $c_i × n_h \\times n_w$\n",
    "- 核**W**: $c_o \\times c_i × k_h × k_w$\n",
    "- 偏差 **B**: $c_o\\times c_i$\n",
    "- 输出**Y**: $c_o \\times m_h × m_w$\n",
    "$$Y = X ★ W + B$$\n",
    "- 计算复杂度（浮点计算数FLOP） $O(c_ic_ok_hk_wm_hm_w)$ \n",
    "    - [注意]复杂度只关心乘法运算量\n",
    "    - FLOPS(Floating-point Operations Per Second)为每秒所执行的浮点运算次数。它是一个衡量计算机计算能力的量，经常使用在那些需要大量浮点运算的科学运算中。\n",
    "$$c_i=c_o=100, k_h=h_w=5, m_h=m_w=64 ==>> 1G(FLOP) $$\n",
    "- 10层，1M样本，10 TFlops\n",
    "    - CPU：0.15G/s， TF=18h\n",
    "    - GPU：12G/s， TF=14min"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "幻灯片",
  "hide_input": false,
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autoclose": true,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "vscode": {
   "interpreter": {
    "hash": "34418ffb6f02e522390adb0e13441cc75f901cd11cccb4f6f613643b4b4d2a0b"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
